package com.engine.cn.juc;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * ReentrantLock 和 Condition结合使用，可以实行更精准的对线程进行控制
 * 这个需要好好的研究一哈，这是ReentrantLock的使用
 */
public class OrderedLockThread {
    private static final int PRINT_TIMES = 10;
    private static final Lock LOCK = new ReentrantLock();
    private static final Condition CONDITION_A = LOCK.newCondition();
    private static final Condition CONDITION_B = LOCK.newCondition();
    private static final Condition CONDITION_C = LOCK.newCondition();
    private static final String A = "A";
    private static final String B = "B";
    private static final String C = "C";
    private static String current = "A";

    /**
     * 三个线程轮番按顺序打印ABC,每个线程输出10次, 这个实现和性能均不如Semaphore，可以参考SemaphoreOrderedThread，人家是无锁并发
     *
     * @param letter           需要输出的字符
     * @param currentCondition 当前condition
     * @param nextCondition    下一condition
     */
    private static void orderedPrint(String letter, Condition currentCondition, Condition nextCondition) {
        for (int i = 0; i < PRINT_TIMES; i++) {
            LOCK.lock();
            try {
                //判断是否满足打印条件，如果不满足则等待
                while (!current.equals(letter)) {
                    currentCondition.await();
                }

                System.out.println(letter);
                if (letter.equals(A)) {
                    current = B;
                    nextCondition.signal();
                } else if (letter.equals(B)) {
                    current = C;
                    nextCondition.signal();
                } else {
                    current = A;
                    nextCondition.signal();
                }
                //通知下一个condition
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                LOCK.unlock();
            }
        }
    }

    public static void main(String[] args) {
        new Thread(() -> orderedPrint("A", CONDITION_A, CONDITION_B)).start();
        new Thread(() -> orderedPrint("B", CONDITION_B, CONDITION_C)).start();
        new Thread(() -> orderedPrint("C", CONDITION_C, CONDITION_A)).start();
    }
}
